package jogs.games.crazyeights;

import jogs.core.*;
import jogs.games.Game;
import jogs.games.Card;
//import jogs.db.*;
import java.io.Serializable;
import java.util.*;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Logger;
import java.util.logging.Level;

public class CrazyEights extends Game implements Serializable
{

	/** The version of the serialized form of this class. */
	//private static final int MAXPLAYERS = 0;
	private static final int MINPLAYERS = 2;
    private static final long serialVersionUID = 4L;
    private static final Logger logger = Logger.getLogger(CrazyEights.class.getName());
	  public static Handler handler = new ConsoleHandler();
    static
    {
     	logger.addHandler(handler);
    }
    private GameServer					parent;
    private int 						status;
	private ArrayList<Player> 			players;
	private ArrayList<ArrayList<Card>>  hands;
	private ArrayList<Card>				pile;
	private ArrayList<Card>  			deck;
	private Card                        lastCard;
	private char						currentSuit;
	private Player			            currentPlayer;
	private boolean						direction; //true clockwise fales counterclockwise
	private boolean						started;
	//private DB 		   					db;
	
	public CrazyEights(String name, GameServer p, int maxPlayers)
	{
		super.maxPlayers = maxPlayers;
		//db = p.getDB();
		hands = new ArrayList<ArrayList<Card>>();
		deck = null;
		pile = new ArrayList<Card>();
		players = new ArrayList<Player>();
		currentPlayer = null;
		super.setName(name);
		direction = true;
		started = false;
		parent = p;
		status = 1;
	}
	
	public void move(Player player, String move) 
	{
		int numCardsToDraw = 0; //number of cards next player will draw.
		int position = players.indexOf(player);
		if(position < 0)
		{
			player.send("@ERROR:not a valid player");
			return;
		}
		ArrayList<Card> hand = hands.get(position);
		
		int begin = move.indexOf('@');
		int end = move.indexOf(':');
		
		//if @ and : are not in message than it is not a command
		if(begin == -1 || end == -1)
		{
			player.send("@ERROR:Not a valid move");
			return;
		}
		
		//get the command out of the message
		String play = move.substring(begin, end);
		
		if(play.equals("@PLAY"))
		{
			if(!player.equals(currentPlayer))
			{
				player.send("@ERROR:Not your turn");
				return;
			}
			String cardsplayed = player.getName() + " plays ";
			int last = move.lastIndexOf(':');
			if(last < 0 || last == end)
			{
				player.send("@ERROR:not a valid command");
				return;
			}
			System.out.println("END: " + end + " LAST: " + last);
			String cards = move.substring(end + 1, last);
			
			//String chosenSuit = null;
			if((move.length()-1) > last) 
				currentSuit = move.charAt(last + 1);
			Card cardarray[];
			cardarray = toCardArray(cards);
			
			if (cardarray[0] == null || (cardarray[0].getRank() != lastCard.getRank() && cardarray[0].getSuit() != currentSuit && cardarray[0].getRank() != 8))
			{	
				player.send("@ERROR:NOT VALID CARD 1");
				return;
			}
			/*if(cardarray.length > 1 && cardarray[0].getRank() != lastCard.getRank() && cardarray[0].getRank() != 1 )
			{
				p.send("@ERROR:NOT VALID MOVE");
				return;
			}*/
			//if player plays an ace allow player to play all 
			//cards in his hand of that suit
			if (cardarray[0].getRank() == 1)
			{
				char matchsuit = cardarray[0].getSuit();
				int matchrank = cardarray[0].getRank();
				
				for(Card c : cardarray)
				{
					if(c != null && c.getSuit() ==	matchsuit)
					{
						matchrank = c.getRank();
					}
					else if(c != null && c.getRank() == matchrank)
					{
						if(matchrank == 1)
							matchsuit = c.getSuit();
						else
							matchsuit = 'e';//suit broken, can no longer match suit.
					}
					else 
					{
						player.send("@ERROR:NOT VALID MOVE");
						return;
					}
					if(!hand.contains(c))
					{
						player.send("@ERROR:CARD NOT IN HAND");
						return;
					}
					cardsplayed += c + ",";
					lastCard = c;
				}
			}
			else if(cardarray[0].getRank() == 2) //if player plays a 2 next player draws 2 or 4 cards
			{
				numCardsToDraw = 0;
				
				for(Card card : cardarray)
				{
					if(card.getRank() != 2 || !hand.contains(card))//make sure all cards in hand are 2s
					{
						player.send("@ERROR:Not a Valid Card 2");
						return;
					}
					if(card.getSuit() == 's')
						numCardsToDraw += 4;
					else
						numCardsToDraw += 2;
					lastCard = card;
					cardsplayed += card + ",";
				}
			}
			else if(cardarray[0].getRank() == 10)
			{
				for(Card card : cardarray)
				{
					if(card.getRank() != 10 || !hand.contains(card))
					{
						player.send("@ERROR:Not a Valid Card 3");
						return;
					}
					direction = !direction;
					cardsplayed += card + ",";
					lastCard = card;
				}
				numCardsToDraw = 0;				
			}
			else if(cardarray[0].getRank() == 11)
			{
				for(Card card : cardarray)
				{
					if(card.getRank() != 11 || !hand.contains(card))
					{
						player.send("@ERROR:Not a Valid Card 4");
						return;
					}
					if(direction)
						position++;
					else
						position--;
					cardsplayed += card + ",";
					lastCard = card;
				}
			}
			else
			{
				for(int i=0; i<cardarray.length; i++)
				{
					if(i == 0)
					{
						if((cardarray[i].getRank() != lastCard.getRank() && cardarray[i].getSuit() != lastCard.getSuit() && cardarray[i].getRank() != 8) || !hand.contains(cardarray[i]))
						{
							player.send("@ERROR:Not a Valid Card 5");
							return;
						}
					}
					else
					{
						if(cardarray[i].getRank() != lastCard.getRank()  || !hand.contains(cardarray[i]))
						{
							player.send("@ERROR:Not a Valid Card 6");
							return;
						}
						
					}
					cardsplayed += cardarray[i] + ",";
					lastCard = cardarray[i];
				}
			}
			//Valid play, remove cards from hand and place them into the discard pile
			for(Card card : cardarray)
			{
				hand.remove(card);
				pile.add(card);
			}
			
			if(lastCard.getRank() != 8)
				currentSuit = lastCard.getSuit();
			
			this.sendPlay("@GAME:" + cardsplayed);
			
			if(direction)
				position++;
			else
				position--;
		}
		else if (play.equals("@DRAW")) //play draws
		{	
			if(!player.equals(currentPlayer))
			{
				player.send("@ERROR:Not your turn");
				return;
			}
			Card card = draw();
			player.send("@GAME:@DRAW:" + card.toString());
			hand.add(card);
			this.sendPlay("@GAME:@DRAWS:" + player.getName());
			position++;
		}
		else if (play.equals("@DEAL")) //deal new deck
		{
			if(currentPlayers < MINPLAYERS)
			{
				player.send("@ERROR:not enough players yet");
				return;
			}
			deal(8);
			position = -1;
		}
		else
		{
			player.send("@ERROR:Not a Valid move");
			return;
		}
		
		if(hand.size() == 0)
		{
			this.sendPlay(player.getName() + " has won");
			hands = new ArrayList<ArrayList<Card>>();
			for(int i = 0; i < players.size(); i++)
			{
				hands.add(new ArrayList<Card>());
			}
			pile = new ArrayList<Card>();
			deck = null;
			started = false;
			return;
		}
		
		if(position >= players.size())
		{
			while (position >= players.size())
				position = position - players.size();
		}
		else if (position < 0)
		{	
			while(position < 0)
				position = players.size() + position;
		}
		
		currentPlayer = players.get(position);
		ArrayList<Card> nextHand = hands.get(position);
		Player nextPlayer = currentPlayer;
		//logger.log(Level.INFO, Integer.toString(position));
		//next player has to draw cards.
		for (int i=0; i<numCardsToDraw; i++)
		{
			Card dc = draw();
			nextPlayer.send("@GAME:@DRAW:" + dc.toString());
			nextHand.add(dc);
		}
		this.sendPlay("@DRAWS:" + nextPlayer.getName() + ":" + numCardsToDraw + ":");
		this.sendPlay("@SUIT" + currentSuit);
		this.sendPlay("@RANK" + lastCard.getRank());
		//let everybody know who the next player is.
		this.sendPlay("@TURN:" + nextPlayer.getName());
	}
	
	public void chat(String message, Player p)
	{
		for(Player player : players)
		{
			player.send("@CHAT:" + p.getPlayerName() + ":" + message);
		}
	}
	
	private Card draw()
	{
		Card r = deck.remove(0);
		if(deck.size() == 0)
		{
			deck = pile;
			Collections.shuffle(deck);
			pile = new ArrayList<Card>();
		}
		return r;
	}
	
	private void deal(int numCards)
	{
		logger.log(Level.INFO, "new Deal");
		deck = Card.newDeck();
		for(int i = 0; i < hands.size(); i++)
		{
			ArrayList<Card> hand = hands.get(i);
			Player p = players.get(i);
			String cards = "@DRAW:";
			for(int j = 0; j < numCards; j++)
			{
				Card c = deck.remove(0);
				hand.add(c);
				cards += c + ",";
			}
			p.send("@GAME:" + cards);
			//logger.log(Level.INFO, p.getName() + " picked up " + cards);
		}
		lastCard = deck.remove(0);
		currentSuit = lastCard.getSuit();
		pile.add(lastCard);
		this.sendPlay("@FLIP:" + lastCard);
		started = true;
		parent.statusChange(gameName);
		status = 3;
		//parent.updateStatus(3);
	}
	
	private void sendPlay(String play)
	{
		for(Player p : players)
		{
			p.send("@GAME:" + play);
		}
	}
	
	
	/**
	*when a player joins the game
	*@param Player who joins
	*@return boolean, true if game has not started yet. fales if game already started
	*/
	public boolean join(Player player)
	{
		if (started)
		{
			player.send("@ERROR:Game has Already Stated");
			return false;
		}
		if(currentPlayers == super.maxPlayers)
		{
			player.send("@ERROR:Table is Full");
			return false;
		}
		String playerNames = "@NAMES:";
		for(Player p : players)
		{
			playerNames += p.getPlayerName() + ":";
			p.send("@GAME:@JOINED:" + player.getPlayerName());
		}
		player.send("@GAME:" + playerNames);
		players.add(player);
		hands.add(new ArrayList<Card>());
		logger.log(Level.INFO, player.getName() + " has joined game " + this.getName());
		//db.addPlayer(player.getName(), ++currentPlayers);
		++currentPlayers;
		return true;
	}
	
	/**
	 * When a player leaves the game.
	 * @param Player who leaves the gam
	 */
	public void leave(Player player)
	{
		int index = players.indexOf(player);
		if (index < 0)
			return;
		
		players.remove(index);
		hands.remove(index);
		currentPlayers--;
		this.sendPlay("@LEFT:" + player.getName());
		logger.log(Level.INFO, player.getName() + " has left the game " + this.getName());
	}
	
	public String getStatus()
	{
		String r = "";
		r += this.getName() + ",";
		r += "crazyeights" + ",";
		r += this.getMaxPlayers() + ",";
		r += players.size() + ",";
		
		r += status;
		
		for(Player p : players)
		{
			r += "," + p.getName();
		}
		return r;
	}
	
	public int getNumPlayers()
	{
		return players.size();
	}
	
	public void close()
	{
		players = null;
		hands = null;
		currentPlayer = null;
		deck = null;
		lastCard = null;
		status = 4;
		//db.updateStatus(4);
		//db = null;
		//parent.removeGame(this.getName());
	}
	
	public static Card[] toCardArray(String cards)
	{
		//look into using split method instead
		int i = 0;
		StringTokenizer tok = new StringTokenizer(cards, ",");
		Card[] ret = new Card[tok.countTokens()];;
		while(tok.hasMoreTokens())
		{
			ret[i++] = Card.createCard(tok.nextToken());
				
		}
		return ret;
	}
	
}



